//
//  NeoNuiSwiftManager.swift
//  GLUZSwift
//
//  Created by xyanl on 2025/4/14.
//

import UIKit
import SwiftyUserDefaults
import ZegoExpressEngine

enum NeoNuiType {
    case zh
    case fileZh
    case ru
    case fileRu
}

enum NeoNuiState {
    case open
    case pause
    case close
}

protocol NeoNuiSwiftDelegate {
    func neoNuiSpeechTranscriber(text: String, type: NeoNuiType, isEnd: Bool)
    
    func neoNuiAudioStateChanged(_ state: NeoNuiState)
    
    func neoNuiRmsChanged(_ rms: Float)
}

class NeoNuiSwiftManager: NSObject {
    
    var delegate: NeoNuiSwiftDelegate?
    var type: NeoNuiType = .zh
    
    static let shared = NeoNuiSwiftManager()
    
    private lazy var neoNui = {
        let neoNui = NeoNui.get_instance()!
        neoNui.delegate = self
        return neoNui
    }()
    
    private var isRunning: Bool = false
    
    private var emptyCount = 0
    /// 麦克风收录的音频数据
    private var recordedVoiceData = Data()
    /// 同步锁对象
    private let recordedVoiceDataLock = NSObject()
    
    func initNeoNui() {
        // 请注意此处的参数配置，其中账号相关需要按照genInitParams的说明填入后才可访问服务
        let result = neoNui.nui_initialize(genParams(), logLevel: NUI_LOG_LEVEL_NONE, saveLog: false)
        xLog("[NeoNui] 初始化结果: \(result)")
        neoNui.nui_set_params(configParams())
        xLog("[NeoNui] 开启")
    }
    
    /// 开始小牛
    func startNeoNui() {
        neoNui.nui_dialog_start(MODE_P2T, dialogParam: configParams())
    }
    
}

private extension NeoNuiSwiftManager {
    
    private func genParams() -> [CChar]? {
        // let token = Defaults[\.token]
        var params: [String: Any] = [
            "app_key": "default",//"t1hm7OdtHN0N5m5q",
            // 获取临时 token https://nls-portal.console.aliyun.com/overview
            // @"token": @"8eee6741ef574a31928913b745297978",
            "token": "default",//token,
            //当初始化SDK时的save_log参数取值为true时，该参数生效。表示是否保存音频debug，该数据保存在debug目录中，需要确保debug_path有效可写
            "save_wav": "false",
            "service_mode": "1",
        ]
        
        switch type {
        case .zh:
            params["url"] = "wss://1an04la087286.vicp.fun/wsttrans/ws/zh"
            
        case .fileZh:
            break
        case .ru:
            params["url"] = "wss://1an04la087286.vicp.fun/wsttrans/ws/ru"
            
        case .fileRu:
            break
        }
        
        // 方式 2：直接获取可选项（注意：可能返回 nil）
        if let cString = params.toJSONString()?.cString(using: .utf8) {
            // cString 类型是 [CChar]，且已自动添加 NULL 终止符
            return cString
        }
        
        return nil
    }
    
    private func configParams() -> [CChar]? {
        // 参数可根据实际业务进行配置
        // 接口说明可见https://help.aliyun.com/document_detail/173528.html
        let config: [String: Any] = [
            // 是否返回中间识别结果
            "enable_intermediate_result": true,
            // 是否在后处理中添加标点
            "enable_punctuation_prediction": true,
            // ITN（逆文本inverse text normalization）中文数字转换阿拉伯数字。设置为True时，中文数字将转为阿拉伯数字输出，默认值：False。
            "enable_inverse_text_normalization": true,
            "sample_rate": 16000,
            "sr_format": "pcm",
        ]
        
        let params: [String: Any] = [
            "nls_config": config,
            "service_type": 4
        ]
        
        // 直接获取可选项（注意：可能返回 nil）
        if let cString = params.toJSONString()?.cString(using: .utf8) {
            // cString 类型是 [CChar]，且已自动添加 NULL 终止符
            return cString
        }
        
        return nil
    }
    
}


// MARK: - 音频采集
extension NeoNuiSwiftManager: ZegoAudioDataHandler {
    
    // 开始音频采集
    func startAudioDataObserver() {
        // 需要的音频数据类型 Bitmask，此处示例四个回调都开启
        let bitmask: ZegoAudioDataCallbackBitMask = [.captured, .player]
        let frameParam = ZegoAudioFrameParam()
        frameParam.channel = .mono
        frameParam.sampleRate = .rate16K
        // 开启获取原始音频数据功能
        ZegoExpressEngine.shared().startAudioDataObserver(bitmask, param: frameParam)
        ZegoExpressEngine.shared().setAudioDataHandler(self)
        // 开/关自动增益控制
        ZegoExpressEngine.shared().enableAGC(true)
        // 是否开启回声消除。
        ZegoExpressEngine.shared().enableAEC(true)
        // 开/关噪声抑制
        ZegoExpressEngine.shared().enableANS(true)
        // 开/关瞬态噪声抑制
        ZegoExpressEngine.shared().enableTransientANS(true)
        
    }
    
    // 停止音频采集
    func stopAudioDataObserver() {
        xLog("[NeoNui] 停止音频采集")
        
        // 异步停止，避免阻塞调用线程
        DispatchQueue.global(qos: .userInitiated).async {
            xLog("[NeoNui] 开始异步停止音频采集")
            
            // 在后台线程执行耗时的清理操作
            self.neoNui.nui_dialog_cancel(false)
            self.neoNui.nui_release()
            ZegoExpressEngine.shared().stopAudioDataObserver()
            self.isRunning = false
            
            xLog("[NeoNui] 异步停止音频采集完成")
        }
    }
    
    // MARK: ZegoAudioDataHandler
    func onCapturedAudioData(_ data: UnsafePointer<UInt8>, dataLength: UInt32, param: ZegoAudioFrameParam) {
        // 本地采集音频数据，推流后可收到回调
        // let sendData = Data.init(bytes: data, count: Int(dataLength))
        objc_sync_enter(recordedVoiceDataLock)
        recordedVoiceData.append(data, count: Int(dataLength))
        objc_sync_exit(recordedVoiceDataLock)
    }
    
}


extension NeoNuiSwiftManager: NeoNuiSdkDelegate {
    
    func onNuiEventCallback(_ nuiEvent: NuiCallbackEvent, dialog: Int, kwsResult wuw: UnsafePointer<CChar>!, asrResult asr_result: UnsafePointer<CChar>!, ifFinish finish: Bool, retCode code: Int32) {
        switch nuiEvent {
        case EVENT_TRANSCRIBER_COMPLETE: break
        case EVENT_SENTENCE_END:
            // 实时语音识别事件，检测一句话结束，返回一句的完整结果。
            let result = String(cString: asr_result)
            let dic = result.toDictionary()!
            
            if let payloadDic = dic["payload"] as? [String: Any] {
                if let res = payloadDic["result"] as? String {
                    xLog("[NeoNui] 结果: \(res)");
                    delegate?.neoNuiSpeechTranscriber(text: res, type: type, isEnd: true)
                }
            }
            
        case EVENT_ASR_PARTIAL_RESULT:
            // 实时语音识别事件，检测一句话结束，返回一句的完整结果。
            let result = String(cString: asr_result)
            let dic = result.toDictionary()!
            
            if let payloadDic = dic["payload"] as? [String: Any] {
                if let res = payloadDic["result"] as? String {
                    delegate?.neoNuiSpeechTranscriber(text: res, type: type, isEnd: false)
                    xLog("[NeoNui] 中间结果: \(res)");
                }
            }
            
        default:
            break
        }
        
        //finish 为真（可能是发生错误，也可能是完成识别）表示一次任务生命周期结束，可以开始新的识别
        if finish {
            DispatchQueue.main.async {
                xLog("[NeoNui] finish 为真（可能是发生错误，也可能是完成识别）表示一次任务生命周期结束，可以开始新的识别")
            }
        }
    }
    
    
    func onNuiNeedAudioData(_ audioData: UnsafeMutablePointer<CChar>!, length len: Int32) -> Int32 {
        autoreleasepool {
            objc_sync_enter(recordedVoiceDataLock)
            defer { objc_sync_exit(recordedVoiceDataLock) }
            
            guard !recordedVoiceData.isEmpty else {
                emptyCount += 1
                if emptyCount >= 50 {
                    print("_recordedVoiceData length = \(recordedVoiceData.count)! empty 50 times.")
                    emptyCount = 0
                }
                return 0
            }
            
            let recorderLen = min(recordedVoiceData.count, Int(len))
            let range = 0..<recorderLen
            
            // 复制数据到 audioData
            recordedVoiceData.copyBytes(
                to: UnsafeMutableRawBufferPointer(start: audioData, count: recorderLen),
                from: range
            )
            
            // 移除已处理的数据
            recordedVoiceData.removeSubrange(range)
            emptyCount = 0
            
            return Int32(recorderLen)
        }
    }
    
    // 根据音频状态进行录音功能的开关。
    func onNuiAudioStateChanged(_ state: NuiAudioState) {
        var neoNuiState: NeoNuiState = .open
        switch state {
        case STATE_OPEN:
            recordedVoiceData = Data()
            neoNuiState = .open
            xLog("[NeoNui] onNuiAudioStateChanged STATE_OPEN \(state)")
            
        case STATE_PAUSE:
            neoNuiState = .pause
            xLog("[NeoNui] onNuiAudioStateChanged STATE_PAUSE \(state)")
            
        case STATE_CLOSE:
            neoNuiState = .close
            xLog("[NeoNui] onNuiAudioStateChanged STATE_CLOSE \(state)")
            
        default: break
        }
        
        delegate?.neoNuiAudioStateChanged(neoNuiState)
    }
    
    
    func onNuiRmsChanged(_ rms: Float) {
        // 音频能量事件。
        // xLog("[NeoNui] onNuiRmsChanged rms=\(rms)")
        DispatchQueue.main.async {
            self.delegate?.neoNuiRmsChanged(rms)
        }

    }
    
}
